package com.yc.vote.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据库操作工具类
 * @author 3456
 * Company 源辰
 * @date 2023年3月5日
 */
public class DBUtil {
	/**
	 * 更新操作
	 * @param con
	 * @param sql
	 * @param params
	 * @return
	 */
	public int update(Connection con, String sql, List<Object> params) {
		if(params == null || params.isEmpty()) {
			return this.update(con, sql);
		}
		return this.update(con, sql, params.toArray());
	}
													/*任意个参数，可0个*/
	public int update(Connection con, String sql, Object ... params) {
		int result = 0;
		PreparedStatement pstmt = null;
		
		try {
			pstmt = con.prepareStatement(sql);
			setParams(pstmt, params);
			result = pstmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			this.close(null, pstmt);
		}
		
		return result;
	}

	/**
	 * 给占位符赋值
	 * @param pstmt
	 * @param params
	 */
	private void setParams(PreparedStatement pstmt, Object ... params) {
		if(params == null || params.length <= 0) {
			return;
		}
		
		for(int i = 0, len = params.length; i < len ; i++) {
			try {
				pstmt.setObject(i + 1, params[i]);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 关闭资源
	 * @param object
	 * @param pstmt
	 */
	private void close(ResultSet rs, PreparedStatement pstmt) {
		if(rs != null) {	
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		if(pstmt != null) {	
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 统计
	 * @param con
	 * @param sql
	 * @param params
	 * @return
	 */
	public Integer total(Connection con, String sql, List<Object> params) {
		if (params == null || params.size() <= 0) {
			return this.total(con, sql);
		}
		return this.total(con, sql, params.toArray());
	}
	public Integer total(Connection con, String sql, Object ...params) {
		int result = 0;
		
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			pstmt = con.prepareStatement(sql); // 预编译执行语句
			this.setParams(pstmt, params);
			rs = pstmt.executeQuery();
			if(rs.next()) {
				result = rs.getInt(1);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			this.close(rs, pstmt);
		}
		return result;
	}
	
	/**
	 * 返回一个map对象的查询方法
	 * @param con
	 * @param sql
	 * @param params
	 * @return
	 */
	public Map<String, Object> findOne(Connection con, String sql, List<Object> params){
		if(params == null || params.size() <= 0) {
			return this.findOne(con, sql);
		}
		return this.findOne(con, sql, params.toArray());
	}
	
	public Map<String, Object> findOne(Connection con, String sql, Object ...params){
		List<Map<String, Object>> result = this.findList(con, sql, params);
		
		if(result == null || result.isEmpty()) {
			return Collections.emptyMap(); // 说明没有找到数据，则返回一个空集合
		}
		return result.get(0);
	}
	
	/**
	 * 返回多行数据的map对象
	 * @param con
	 * @param sql
	 * @param params
	 * @return
	 */
	public List<Map<String, Object>> findList(Connection con, String sql, List<Object> params) {
		if(params == null || params.size() <= 0) {
			return this.findList(con, sql);
		}
		return this.findList(con, sql, params.toArray());
	}
	
	public List<Map<String, Object>> findList(Connection con, String sql, Object ...params) {
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		Map<String, Object> map = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			pstmt = con.prepareStatement(sql);
			this.setParams(pstmt, params);
			rs = pstmt.executeQuery();
			List<String> columnNames = this.getColumnNames(rs); // 获取结果集中列名
			while(rs.next()) {
				map = new HashMap<String, Object>();
				for(String columnName : columnNames ) { // 通过列名获取每一列的值
					map.put(columnName, rs.getObject(columnName));
				}
				list.add(map);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			this.close(rs, pstmt);
		}
		return list;
	}
	
	/**
	 * 从指定结果集中获取类名的方法
	 * @param rs
	 * @return
	 */
	private List<String> getColumnNames(ResultSet rs){
		List<String> columnNames = new ArrayList<String>();
		try {
			ResultSetMetaData rsmd = rs.getMetaData(); // 获取结果集元数据
			int count = rsmd.getColumnCount(); // 获取结果集中列数量
			for(int i = 1 ; i <= count ; i++) {
				columnNames.add(rsmd.getColumnName(i).toLowerCase()); // 获取列的名称，并将列名转成小写
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return columnNames;
	}
	
	/**
	 * 基于实体类对象
	 * @param <T>
	 * @param con
	 * @param c
	 * @param sql
	 * @param params
	 * @return
	 */
	public <T> T findOne(Connection con, Class<?> c, String sql, List<Object> params){
		if(params == null || params.size() <= 0) {
			return this.findOne(con, c, sql);
		}
		return this.findOne(con, c, sql, params.toArray());
	}
	
	public <T> T findOne(Connection con, Class<?> c, String sql, Object ...params){
		List<T> list = this.findList(con, c, sql, params);
		
		if(list == null || list.isEmpty()) {
			return null; 
		}
		return list.get(0);
	}
	
	public <T> List<T> findList(Connection con, Class<?> c, String sql, List<Object> params) {
		if(params == null || params.size() <= 0) {
			return this.findList(con, c , sql);
		}
		return this.findList(con, c, sql, params.toArray());
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> findList(Connection con, Class<?> c, String sql, Object ...params) {
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<T> result = new ArrayList<T>();
		
		try {
			pstmt = con.prepareStatement(sql); // 预编译执行语句
			this.setParams(pstmt, params);
			rs = pstmt.executeQuery();
			List<String> columnNames = this.getColumnNames(rs); // 获取列名
			T t = null;
			
			//列名对应对象中的一个属性名，那么要将这个列的值注入到对应的属性中，就需要获得对应的setter方法
			Map<String, Method> setters = this.getSetter(c);
			
			String methodName = null;
			Method method = null;
			Class<?>[] types = null;
			Class<?> type = null;
			String obj = null;
			
			while(rs.next()) {
				t = (T) c.newInstance(); // 实例化一个类的对象
				for(String columnName : columnNames) { // 循环取出每一个列的值注入到对应的属性中
					obj = rs.getString(columnName); // 以字符串的方式取出这个列的值
					if(StringUtil.checkNull(obj)) { // 说明这一列没有数据
						continue;
					}
					
					// 注值需要调用属性的setter方法
					methodName = "set" + columnName;
					method = setters.get(methodName); // 根据方法名找对应的方法
					if(method == null) { // 说明这个属性没有提供setter方法，则这个属性无法注值
						continue;
					}
					
					//如果有，则需要激活这个方法注值
					//先获取这个方法的形参列表
					types = method.getParameterTypes();
					if(types == null || types.length <= 0) { // 说明这不是一个标准的setter方法
						continue;
						
					}
					
					type = types[0]; // 取出这个方法的第一个形参的类型
					if(type == Integer.TYPE) {
						method.invoke(t, Integer.parseInt(obj)); // 则将这个对象转成整型
						continue;
					}
					
					if(type == Integer.class) {
						method.invoke(t, Integer.valueOf(obj)); // 则将这个对象转成整型
						continue;
					}
					
					if(type == Double.TYPE) {
						method.invoke(t, Double.parseDouble(obj)); 
						continue;
					}
					
					if(type == Double.class) {
						method.invoke(t, Double.valueOf(obj)); 
						continue;
					}
					
					if(type == Long.TYPE) {
						method.invoke(t, Long.parseLong(obj)); 
						continue;
					}
					
					if(type == Long.class) {
						method.invoke(t, Long.valueOf(obj)); 
						continue;
					}
					
					method.invoke(t, obj);
				}
				result.add(t);
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}finally {
			this.close(rs, pstmt);
		}
		return result;
	}
	
	/**
	 * 获取指定类中的setter方法
	 * @param c
	 * @return
	 */
	private Map<String, Method> getSetter(Class<?> c) {
		Method[] methods = c.getDeclaredMethods();
		if(methods == null || methods.length <= 0) {
			return Collections.emptyMap();
		}
		
		Map<String, Method> setters = new HashMap<String, Method>();
		String methodName = null;
		for(Method method : methods) {
			methodName = method.getName();
			if(methodName.startsWith("set")) {
				setters.put(methodName.toLowerCase(), method);
			}
		}
		return setters;
	}
}
